package com.jprocms.module.cms.controller.admin.accessstatistics;

import com.github.pagehelper.Page;
import com.jpro.framework.common.pojo.CommonResult;
import com.jpro.framework.common.pojo.PageResult;
import com.jprocms.module.cms.controller.admin.accesspage.vo.AccessPagePageReqVO;
import com.jprocms.module.cms.controller.admin.accesspage.vo.AccessStatReqVO;
import com.jprocms.module.cms.convert.PageConverter;
import com.jprocms.module.cms.dal.dataobject.accesspage.AccessPageDO;
import com.jprocms.module.cms.dal.dataobject.accessstatistics.AccessStatisticsDO;
import com.jprocms.module.cms.dal.dataobject.accesstrend.AccessTrendDO;
import com.jprocms.module.cms.dal.dataobject.site.SiteDO;
import com.jprocms.module.cms.service.accesspage.AccessPageService;
import com.jprocms.module.cms.service.accessstatistics.AccessStatisticsService;
import com.jprocms.module.cms.service.accesstrend.AccessTrendService;
import com.jprocms.module.cms.util.CmsContextUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.lang.Nullable;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDate;
import java.time.OffsetDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import static com.jprocms.module.cms.dal.dataobject.accessstatistics.AccessStatisticsDO.*;


/**
 * 访问统计 Controller
 *
 * @author jprocms
 */
@Tag(name = "管理后台 - 统计")
@RestController
@RequestMapping("/cms/accessstatistic")
@Validated
public class AccessStatisticController {
    private final AccessTrendService visitTrendService;
    private final AccessStatisticsService visitStatService;

    private final AccessPageService accessPageService;
    private final PageConverter pageConverter;

    public AccessStatisticController(AccessTrendService visitTrendService, AccessStatisticsService visitStatService,
                                     AccessPageService accessPageService, PageConverter pageConverter) {
        this.visitTrendService = visitTrendService;
        this.visitStatService = visitStatService;
        this.accessPageService = accessPageService;
        this.pageConverter = pageConverter;
    }

    @Operation(summary = "趋势统计")
    @GetMapping("trend-stat")
    public CommonResult<List<AccessTrendDO>> trendStat(
            @RequestParam(name = "begin", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) OffsetDateTime begin,
            @RequestParam(name = "end", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) OffsetDateTime end) {
        SiteDO site = CmsContextUtil.getSite();
        // 12 时
        int maxForMinute = 12 * 60 * 60;
        // 6 天
        int maxForHour = 6 * 24 * 60 * 60;
        // 6 月
        int maxForDay = 6 * 30 * 24 * 60 * 60;
        if (end == null) {
            end = OffsetDateTime.now();
        }
        long second = end.toEpochSecond() - (begin != null ? begin.toEpochSecond() : 0);
        if (second >= maxForDay) {
            final List<AccessTrendDO> accessTrendDOS = visitTrendService.selectFullList(site.getId(), AccessTrendDO.PERIOD_MONTH,
                    Optional.ofNullable(begin).map(date -> date.format(MONTH_FORMATTER)).orElse(null),
                    end.format(MONTH_FORMATTER));
            return CommonResult.success(accessTrendDOS);
        } else if (second >= maxForHour) {
            final List<AccessTrendDO> accessTrendDOS = visitTrendService.selectFullList(site.getId(), AccessTrendDO.PERIOD_DAY,
                    Optional.ofNullable(begin).map(date -> date.format(DAY_FORMATTER)).orElse(null), end.format(DAY_FORMATTER));
            return CommonResult.success(accessTrendDOS);
        } else if (second >= maxForMinute) {
            final List<AccessTrendDO> accessTrendDOS = visitTrendService.selectFullList(site.getId(), AccessTrendDO.PERIOD_HOUR,
                    Optional.ofNullable(begin).map(date -> date.format(HOUR_FORMATTER)).orElse(null),
                    end.format(HOUR_FORMATTER));
            return CommonResult.success(accessTrendDOS);
        } else {
            final List<AccessTrendDO> accessTrendDOS = visitTrendService.selectFullList(site.getId(), AccessTrendDO.PERIOD_MINUTE,
                    Optional.ofNullable(begin).map(date -> date.format(MINUTE_FORMATTER)).orElse(null),
                    end.format(MINUTE_FORMATTER));
            return CommonResult.success(accessTrendDOS);
        }
    }

    @Operation(summary = "访客分析")
    @GetMapping("visitor-stat")
    public CommonResult<Map<String, AccessStatisticsDO>> visitorStat(
            @RequestParam(name = "begin", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate begin,
            @RequestParam(name = "end", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate end) {
        List<AccessStatisticsDO> list = stat(TYPE_VISITOR, begin, end);
        String newVisitorName = "1";
        String oldVisitorName = "0";
        AccessStatisticsDO newVisitor = list.stream().filter(item -> newVisitorName.equals(item.getStatName())).findFirst()
                .orElse(new AccessStatisticsDO());
        AccessStatisticsDO oldVisitor = list.stream().filter(item -> oldVisitorName.equals(item.getStatName())).findFirst()
                .orElse(new AccessStatisticsDO());
        Map<String, AccessStatisticsDO> result = new HashMap<>(16);
        result.put("newVisitor", newVisitor);
        result.put("oldVisitor", oldVisitor);
        return CommonResult.success(result);
    }

    @Operation(summary = "地域分布-省份")
    @GetMapping("province-stat")
    public CommonResult<List<AccessStatisticsDO>> provinceStat(
            @RequestParam(name = "begin", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate begin,
            @RequestParam(name = "end", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate end) {
        return CommonResult.success(stat(TYPE_PROVINCE, begin, end));
    }

    @Operation(summary = "地域分布-国家")
    @GetMapping("country-stat")
    public CommonResult<List<AccessStatisticsDO>> countryStat(
            @RequestParam(name = "begin", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate begin,
            @RequestParam(name = "end", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate end) {
        return CommonResult.success(stat(TYPE_COUNTRY, begin, end));
    }

    @Operation(summary = "来源类型")
    @GetMapping("source-type-stat")
    public CommonResult<List<AccessStatisticsDO>> sourceTypeStat(
            @RequestParam(name = "begin", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate begin,
            @RequestParam(name = "end", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate end) {
        return CommonResult.success(stat(TYPE_SOURCE_TYPE, begin, end));
    }

    @Operation(summary = "来源统计")
    @GetMapping("source-stat")
    public CommonResult<PageResult<AccessStatisticsDO>> sourceStatPage(AccessStatReqVO statReqVO) {
        SiteDO site = CmsContextUtil.getSite();
        final Page<AccessStatisticsDO> accessStatisticsDOPage = visitStatService.statByDate(site.getId(), TYPE_SOURCE_TYPE,
                statReqVO.getDateString() != null && statReqVO.getDateString().length > 0 && statReqVO.getDateString()[0] != null ? statReqVO.getDateString()[0].format(DAY_FORMATTER) : null,
                statReqVO.getDateString() != null && statReqVO.getDateString().length > 1 && statReqVO.getDateString()[1] != null ? statReqVO.getDateString()[1].format(DAY_FORMATTER) : null,
                statReqVO.getPageNo(), statReqVO.getPageSize());
        return CommonResult.success(pageConverter.page(accessStatisticsDOPage));
    }

    //受访页面
    @Operation(summary = "入口页面统计")
    @GetMapping("entry-page-stat")
    public CommonResult<PageResult<AccessPageDO>> entryPageStat(AccessPagePageReqVO reqVO) {
        reqVO.setSiteId(CmsContextUtil.getCurrSiteId());
        reqVO.setAccessType(AccessPageDO.TYPE_ENTRY_URL);
        reqVO.setDateStringBegin(reqVO.getDateString() != null && reqVO.getDateString().length > 0 && reqVO.getDateString()[0] != null ? reqVO.getDateString()[0].format(DAY_FORMATTER) : null);
        reqVO.setDateStringEnd(reqVO.getDateString() != null && reqVO.getDateString().length > 1 ? reqVO.getDateString()[1].format(DAY_FORMATTER) : null);
        final PageResult<AccessPageDO> accessPagePage = accessPageService.statPage(reqVO);
        return CommonResult.success(accessPagePage);
    }

    @Operation(summary = "受访页面统计")
    @GetMapping("access-page-stat")
    public CommonResult<PageResult<AccessPageDO>> accessPageStat(AccessPagePageReqVO reqVO) {
        reqVO.setSiteId(CmsContextUtil.getCurrSiteId());
        reqVO.setAccessType(AccessPageDO.TYPE_VISITED_URL);
        reqVO.setDateStringBegin(reqVO.getDateString() != null && reqVO.getDateString().length > 0 && reqVO.getDateString()[0] != null ? reqVO.getDateString()[0].format(DAY_FORMATTER) : null);
        reqVO.setDateStringEnd(reqVO.getDateString() != null && reqVO.getDateString().length > 1 ? reqVO.getDateString()[1].format(DAY_FORMATTER) : null);
        final PageResult<AccessPageDO> accessPagePage = accessPageService.statPage(reqVO);
        return CommonResult.success(accessPagePage);
    }

    //设备 device-sta
    @Operation(summary = "设备统计")
    @GetMapping("device-stat")
    public CommonResult<List<AccessStatisticsDO>> deviceStat(
            @RequestParam(name = "begin", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate begin,
            @RequestParam(name = "end", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate end) {
        return CommonResult.success(stat(TYPE_DEVICE, begin, end));
    }

    //操作系统 os-stat
    @Operation(summary = "操作系统统计")
    @GetMapping("os-stat")
    public CommonResult<List<AccessStatisticsDO>> osStat(
            @RequestParam(name = "begin", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate begin,
            @RequestParam(name = "end", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate end) {
        return CommonResult.success(stat(TYPE_OS, begin, end));
    }

    //浏览器 browser-stat
    @Operation(summary = "浏览器统计")
    @GetMapping("browser-stat")
    public CommonResult<List<AccessStatisticsDO>> browserStat(
            @RequestParam(name = "begin", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate begin,
            @RequestParam(name = "end", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate end) {
        return CommonResult.success(stat(TYPE_BROWSER, begin, end));
    }


    private List<AccessStatisticsDO> stat(Integer type, @Nullable LocalDate begin, @Nullable LocalDate end) {
        SiteDO site = CmsContextUtil.getSite();
        return visitStatService.statByDate(site.getId(), type,
                begin != null ? begin.format(DAY_FORMATTER) : null,
                end != null ? end.format(DAY_FORMATTER) : null);
    }
}
